{
  Copyright 2012 Sergey Ostanin

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
}

unit OrderQuestionEditor;

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ActnList,
  ExtCtrls, ComCtrls, Shape, PadEditor, VisualUtils, superobject, MiscUtils,
  OrderQuestionOptions, QuestionEditors, Questions, OrderQuestion;

type
  TOrderQuestionEditorFrame = class(TShapeFrame, IQuestionEditor)
    actAddElement: TAction;
    actDeleteElement: TAction;
    actSetOptions: TAction;
    actMoveElementDown: TAction;
    actMoveElementUp: TAction;
    alQuestion: TActionList;
    ilQuestion: TImageList;
    pnlFormulation: TPanel;
    pnlElements: TPanel;
    pnlQuestion: TPanel;
    sbxQuestion: TScrollBox;
    tbrQuestion: TToolBar;
    tbrPage: TToolBar;
    tbItems: TToolButton;
    tbDistractors: TToolButton;
    tbAddElement: TToolButton;
    tbDeleteElement: TToolButton;
    tbSeparator1: TToolButton;
    tbMoveElementUp: TToolButton;
    tbMoveElementDown: TToolButton;
    tbSeparator2: TToolButton;
    tbSetOptions: TToolButton;
    procedure actAddElementExecute(Sender: TObject);
    procedure actAddElementUpdate(Sender: TObject);
    procedure actDeleteElementExecute(Sender: TObject);
    procedure actDeleteElementUpdate(Sender: TObject);
    procedure actMoveElementDownExecute(Sender: TObject);
    procedure actMoveElementDownUpdate(Sender: TObject);
    procedure actMoveElementUpExecute(Sender: TObject);
    procedure actMoveElementUpUpdate(Sender: TObject);
    procedure actSetOptionsExecute(Sender: TObject);
    procedure tbDistractorsClick(Sender: TObject);
    procedure tbItemsClick(Sender: TObject);
  private
    FFormulation: TPadEditorFrame;
    FElements: TPadEditorFrameList;
    FOldElementEditorIds: UnicodeString;
    FMaxElements: Integer;
    FSelectElement: Integer;
    FOptionsImageIndex: Integer;
    FCustomOptionsImageIndex: Integer;
    procedure SyncElementEditors;
    procedure RenderPageSelector;
    procedure SelectPage(const Page: String);
    function SelectedElementIndex: Integer;
    procedure MoveElement(Down: Boolean);
    { private declarations }
  public
    procedure AfterConstruction; override;
    destructor Destroy; override;
    procedure Render;
    { public declarations }
  end; 

implementation

{$R *.lfm}
{$R *.rc}

resourcestring
  SOrderQuestion = 'Ordering Question';
  SInitialText = 'Order the items.';

{ TOrderQuestionEditorFrame }

procedure TOrderQuestionEditorFrame.tbItemsClick(Sender: TObject);
begin
  SelectPage('ITEMS');
end;

procedure TOrderQuestionEditorFrame.tbDistractorsClick(Sender: TObject);
begin
  SelectPage('DISTRACTORS');
end;

procedure TOrderQuestionEditorFrame.actAddElementExecute(Sender: TObject);
begin
  Dispatcher.PerformAction(Id, 'addElement');
  FSelectElement := FElements.Count;
end;

procedure TOrderQuestionEditorFrame.actAddElementUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := FElements.Count < FMaxElements;
end;

procedure TOrderQuestionEditorFrame.actDeleteElementExecute(Sender: TObject);
var
  Index: Integer;
  Input: ISuperObject;
begin
  Index := SelectedElementIndex;

  Input := SO;
  Input.I['index'] := Index;
  Dispatcher.PerformAction(Id, 'deleteElement', Input);

  if Index = FElements.Count-1 then
    FSelectElement := Index-1
  else
    FSelectElement := Index;
end;

procedure TOrderQuestionEditorFrame.actDeleteElementUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := SelectedElementIndex <> -1;
end;

procedure TOrderQuestionEditorFrame.actMoveElementDownExecute(Sender: TObject);
begin
  MoveElement(TRUE);
end;

procedure TOrderQuestionEditorFrame.actMoveElementDownUpdate(Sender: TObject);
var
  Index: Integer;
begin
  Index := SelectedElementIndex;
  (Sender as TAction).Enabled := (Index <> -1) and (Index < FElements.Count-1);
end;

procedure TOrderQuestionEditorFrame.actMoveElementUpExecute(Sender: TObject);
begin
  MoveElement(FALSE);
end;

procedure TOrderQuestionEditorFrame.actMoveElementUpUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := SelectedElementIndex > 0;
end;

procedure TOrderQuestionEditorFrame.actSetOptionsExecute(Sender: TObject);
begin
  TOrderQuestionOptionsForm.OpenModal(Id, Shape.Clone, Dispatcher);
end;

procedure TOrderQuestionEditorFrame.SyncElementEditors;
var
  Ids, Id: ISuperObject;
begin
  Ids := Shape['elementEditor'];
  if AssignDiffering(Ids.AsJSon, FOldElementEditorIds) then
  begin
    FElements.Clear;
    for Id in Ids do
      FElements.AddSafely(TPadEditorFrame.Create(Id.AsString, Dispatcher, pnlElements));
  end;
end;

procedure TOrderQuestionEditorFrame.RenderPageSelector;

  procedure SetUpButton(Button: TToolButton; const CountPropertyName: String);
  begin
    if Shape.I[CountPropertyName] > 0 then
      Button.Font.Style := [fsBold]
    else
      Button.Font.Style := [];
  end;

begin
  if Shape.S['page'] = 'ITEMS' then
    tbItems.Down := TRUE
  else
    tbDistractors.Down := TRUE;
  SetUpButton(tbItems, 'itemCount');
  SetUpButton(tbDistractors, 'distractorCount');
end;

procedure TOrderQuestionEditorFrame.SelectPage(const Page: String);
var
  Input: ISuperObject;
begin
  if Page <> Shape.S['page'] then
  begin
    Input := SO;
    Input.S['page'] := Page;
    Dispatcher.PerformAction(Id, 'selectPage', Input);
  end;
end;

function TOrderQuestionEditorFrame.SelectedElementIndex: Integer;
begin
  for Result := 0 to FElements.Count-1 do
    if ContainsActiveFormControl(FElements[Result]) then
      Exit;
  Result := -1;
end;

procedure TOrderQuestionEditorFrame.MoveElement(Down: Boolean);
var
  Input: ISuperObject;
begin
  Input := SO;
  Input.I['index'] := SelectedElementIndex;
  Input.B['forward'] := Down;
  Dispatcher.PerformAction(Id, 'moveElement', Input);
end;

procedure TOrderQuestionEditorFrame.AfterConstruction;
begin
  inherited;
  alQuestion.OnExecute := Scene.ActionListExecuteFilter;
  LoadActionImages(alQuestion);
  MakeVerticallyAutoScrollable(sbxQuestion);

  FFormulation := TPadEditorFrame.Create(Shape.S['formulationEditor'], Dispatcher, pnlFormulation);
  FFormulation.Align := alClient;
  FFormulation.Border := TRUE;

  FElements := TPadEditorFrameList.Create;
  FMaxElements := Shape.I['maxElements'];
  FSelectElement := -1;

  FOptionsImageIndex := AddPngResourceToImageList('OrderQuestionEditor/options.png', ilQuestion);
  FCustomOptionsImageIndex := AddPngResourceToImageList('OrderQuestionEditor/customOptions.png', ilQuestion);
end;

destructor TOrderQuestionEditorFrame.Destroy;
begin
  FreeAndNil(FFormulation);
  FreeAndNil(FElements);
  inherited;
end;

procedure TOrderQuestionEditorFrame.Render;
var
  Element: TPadEditorFrame;
begin
  UpdateShape;

  DisableAutoSizing;
  try
    FFormulation.Render;
    RenderPageSelector;
    SyncElementEditors;
    for Element in FElements do
      Element.Render;
  finally
    EnableAutoSizing;
  end;

  if (FSelectElement >= 0) and (FSelectElement < FElements.Count) then
    FElements[FSelectElement].SelectFirst;
  FSelectElement := -1;

  if Shape.B['hasDefaultOptions'] then
    actSetOptions.ImageIndex := FOptionsImageIndex
  else
    actSetOptions.ImageIndex := FCustomOptionsImageIndex;
end;

initialization

  QuestionRegistry.Add(TOrderQuestion.Kind, SOrderQuestion, 'OrderQuestionEditor/orderQuestion.png', SInitialText);
  QuestionEditorRegistry.Add(TOrderQuestionEditorFrame, $2b81daefffa009a6);

end.

